/****************************************************
          File Name:setuid.c
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: Thu 04 Sep 2014 11:52:50 AM CST
*****************************************************/
#define _GNU_SOURCE
#include<stdio.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/acct.h>

void setacc(void)
{
    acct(NULL);
    if(errno)
        perror("acct");
    else
        printf("\e[43m it works\e[0m\n");
}

void run()
{
        uid_t ruid, euid, suid;

        euid=geteuid();
        ruid=getuid();

        setacc();

        printf("drop privilege for a while\n");
        seteuid(ruid);             // set to angel's uid
        if(errno)
        {
            perror("seteuid 2nd time");
            exit(1);
        }
        getresuid(&ruid,&euid,&suid);
        printf("\e[32mruid:%d, euid: %d, suid: %d\e[0m\n",ruid,euid,suid);

        setacc();

        printf("re-gain privilege\n");
        /*
         * to set-user-id process the following call will succeed
         * but to set-user-id-root process will fail, because after drop root privilege(seteuid(ruid))
         * then this process has no permission to set back the the euid 0
         */
        seteuid(euid);                  // restore to ori-euid 
        if(errno)
        {
            perror("seteuid 2nd time");
            exit(1);
        }
        getresuid(&ruid,&euid,&suid);
        printf("\e[32mruid:%d, euid: %d, suid: %d\e[0m\n",ruid,euid,suid);

        setacc();

        printf("drop privilege permanetly\n");
        setuid(getuid());              // re-set uid and euid
        if(errno)
        {
            perror("seteuid 2nd time");
            exit(1);
        }
        getresuid(&ruid,&euid,&suid);
        printf("\e[32mruid:%d, euid: %d, suid: %d\e[0m\n",ruid,euid,suid);

        setacc();

        printf("raise privilege\n");
        setuid(ruid);
        if(errno)
        {
            perror("seteuid 2nd time");
            exit(1);
        }
        getresuid(&ruid,&euid,&suid);
        printf("\e[32mruid:%d, euid: %d, suid: %d\e[0m\n",ruid,euid,suid);

        setacc();
}

void test(char who)
{
    if(who=='r')
        run();

    if(who==' ')
        run();
    printf("the uid of %s is: %d\n",who=='r'?"root":"angel",getuid());
    printf("the euid of %s is: %d\n",who=='r'?"root":"angel",geteuid());
}

int main()
{
    uid_t ruid, euid, suid;
    
    getresuid(&ruid,&euid,&suid);
    if(errno)
    {
        perror("getresuid");
        exit(1);
    }
    else{
        errno=0;
        /*
         * run as root : ruid: 0 , euid: 0, suid: 0
         *
         * run as angel: ruid: 1000, euid: 0, suid: 0
         */
        printf("original ruid: %d, euid: %d, suid: %d\n",ruid,euid,suid);
    }
   
    printf("getuid() : %d\t geteuid(): %d\n",getuid(),geteuid());

    if(getuid()==0)
    {
        // for root
       test('r');
    }
    else
    {
        // for set-user-id-root
        test(' ');
    }

    return 0;
}
